package tools

import (
	"fmt"
	RawCAN "github.com/linklayer/go-socketcan/pkg/socketcan"
	"runtime"
	"sync"
)

// 该实体不支持广播行为, 每个程序使用请独立开启一个canInstance

type Can struct {
	// 交互的Interfaces
	canIfs []RawCAN.Interface
	// 镜像功能
	mirror bool
	// 镜像功能的fd
	fd *Fd
	// 当前报文指针
	cur *RawCAN.CanFrame
	// hook list
	hookList []func(*RawCAN.CanFrame)
	// 表征有新的报文
	new bool
	// 内部使用的Serialization
	serialization Serialization
}

// CanInit 使用默认通道名称的构造函数
func CanInit() *Can {
	return CanInitWithInterfaces([]string{"can0", "can1", "can2", "can3"})
}

func CanInitWithInterfaces(CanIfsName []string) *Can {
	//filePath := flag.String("f", "mirror.log", "save file path")
	//flag.Parse()
	filePath := "mirror.log"

	canInstance := &Can{
		serialization: &defaultSerialization{},
	}
	canInstance.mirror = false

	// 先读数据再初始化
	canInstance.fd = FdInit(filePath)

	var lock sync.Mutex

	canIfs := make([]RawCAN.Interface, len(CanIfsName))
	for i, v := range CanIfsName {
		If, err := RawCAN.NewRawInterface(v)
		if err != nil {
			panic(fmt.Sprintf("create Interface %v err: %s", v, err))
		}
		canIfs[i] = If
		go func(index uint32, c *Can, I RawCAN.Interface) {
			for {
				frame, err := I.RecvFrame()
				if err != nil {
					fmt.Println("recv Can frame err:", err)
					continue
				}

				lock.Lock()
				frame.ArbId |= index << 12
				c.cur = &frame
				c.new = true
				if c.mirror {
					c.fd.SelfDefinedWriteString(c.serialization.Serialize(&frame))
				}
				for _, v := range c.hookList {
					v(&frame)
				}
				lock.Unlock()
			}
		}(uint32(i), canInstance, If)
	}
	canInstance.canIfs = canIfs
	return canInstance
}

// Recv 供给客户端调用的, 表征从通道到达上位机
func (c *Can) Recv() *RawCAN.CanFrame {
label:
	if !c.new {
		runtime.Gosched()
		goto label
	}
	c.new = false
	return c.cur
}

// Send 供给客户端调用的, 表征从上位机到达通道
func (c *Can) Send(frame RawCAN.CanFrame) error {
	index := frame.ArbId >> 12
	frame.ArbId &= 0x7ff
	return c.canIfs[index].SendFrame(frame)
}

func (c *Can) EnableMirror() {
	c.mirror = true
}

func (c *Can) DisableMirror() {
	c.mirror = false
}

func (c *Can) RegisterHook(a func(*RawCAN.CanFrame)) {
	c.hookList = append(c.hookList, a)
	return
}

func (c *Can) RegisterSerialization(serialization Serialization) {
	c.serialization = serialization
	return
}

// Handler 提醒处理fd
func (c *Can) Handler() {
	c.fd.Handler()
}
